package server

import (
  "errors"
  "fmt"

  "github.com/siddontang/go-log/log"
)

const (
  MYCHAT_MAX_MESSAGE = 100
)

var (
  respInvalidUser    = []byte("user fail invalidUser\r\n")
  respInvalidNode    = []byte("user fail invalidNode\r\n")
  respChatTooBigMsg  = []byte("user fail tooBigMessage\r\n")
  respWrongNameOrPwd = []byte("login fail wrongNameOrPasswd\r\n")
  respAlreadyLogin   = []byte("login fail alreadyLogin\r\n")
  respTooManyNodes   = []byte("login fail tooManyNode\r\n")
  respNodeNoLogin    = []byte("node fail nologin\r\n")
  respUserNoLogin    = []byte("user fail nologin\r\n")

  errWrongNameOrPwd = errors.New("wrong name or password")
  errAlreadyLogin   = errors.New("login fail alreadyLogin")
  errInvalidUser    = errors.New("user fail invalidUser")
  errInvalidNode    = errors.New("user fail invalidNode")
  errNodeNoLogin    = errors.New("node fail nologin")
  errUserNoLogin    = errors.New("user fail nologin")
  errChatTooBigMsg  = errors.New("user fail tooBigMessage")
)

func userLoginCommand(c *requestContext) error {
  username := string(c.args[1])
  password := string(c.args[2])
  // ignore c.args[3], c.args[4]

  log.Debug("UserName: %s, Password: %s", username, password)

  is_valid, err := c.app.dbValidUser(username, password)
  if err != nil {
    // TODO
    // return what to client??
    return err
  }
  if !is_valid {
    c.resp.writeBulk(respWrongNameOrPwd)
    return errWrongNameOrPwd
  }

  c.family = dflFamilyMgr.GetFamilyByNodeName(username)
  if c.family == nil {
    c.resp.writeBulk(respInvalidUser)
    return errInvalidUser
  }
  c.nodeinfo = c.family.GetMember(username)
  if c.nodeinfo == nil {
    c.resp.writeBulk(respInvalidUser)
    return errInvalidUser
  }

  if c.nodeinfo.isLogin {
    c.resp.writeBulk(respAlreadyLogin)
    return errAlreadyLogin
  }
  c.nodeinfo.On(c)

  // login success
  c.resp.writeBulk([]byte(fmt.Sprintf("login ok %s\r\n", c.family.uuid)))

  // get all nodeinfo
  allnodeinfo := c.family.GetAllNodeInfo()
  c.resp.writeBulk(allnodeinfo)

  // get family's all users' info
  allmemberinfo := c.family.GetAllMemberInfo()
  c.resp.writeBulk(allmemberinfo)

  // broadcast login info
  broadcastUserLoginInfo(c)

  return nil
}

func broadcastUserLoginInfo(c *requestContext) {
  if c.family == nil || c.nodeinfo == nil {
    return
  }
  msg := fmt.Sprintf("userinfo ok %s on NULL 0\r\n", c.nodeinfo.name)
  c.family.SendMsgToOtherMembers([]byte(msg), c.nodeinfo)
}

func userSayCommand(c *requestContext) error {
  if c.family == nil || c.nodeinfo == nil || !c.nodeinfo.isLogin {
    c.resp.writeBulk(respUserNoLogin)
    return errUserNoLogin
  }

  var (
    msgReceiver = string(c.args[1])
    msgContent  = c.args[2]
    msgId       = c.args[3]
    qos         = c.args[4]
  )

  if c.nodeinfo.name == string(msgReceiver) {
    c.resp.writeBulk(respInvalidUser)
    return errInvalidUser
  }

  if len(msgContent) > MYCHAT_MAX_MESSAGE {
    c.resp.writeBulk(respChatTooBigMsg)
    return errChatTooBigMsg
  }

  receiver := c.family.GetMember(msgReceiver)
  if receiver == nil {
    c.resp.writeBulk(respUserNoLogin)
    return errUserNoLogin
  }

  msgSend := fmt.Sprintf("user ok %s %s %s %s\r\n", c.nodeinfo.name, msgContent, msgId, qos)
  receiver.req.resp.writeBulk([]byte(msgSend))
  receiver.req.resp.flush()

  return nil
}

func userInfoCommand(c *requestContext) error {
  if c.family == nil || c.nodeinfo == nil || !c.nodeinfo.isLogin {
    c.resp.writeBulk(respUserNoLogin)
    return errUserNoLogin
  }

  // get family's all users' info
  allmemberinfo := c.family.GetAllMemberInfo()
  c.resp.writeBulk(allmemberinfo)

  return nil
}

func userHeartbeatCommand(c *requestContext) error {
  c.resp.writeBulk(respHeartbeat)
  return nil
}

func userCommand(c *requestContext) (err error) {
  if len(c.args) != 5 {
    return ErrCmdParams
  }

  subcmd := string(c.args[0])
  switch subcmd {
  case "login":
    err = userLoginCommand(c)
  case "say":
    err = userSayCommand(c)
  case "userinfo":
    err = userInfoCommand(c)
  case "heartbeat":
    err = userHeartbeatCommand(c)
  case "debug":
    // TODO
    err = nil
  default:
    err = ErrCmdParams
  }

  return
}

func init() {
  register("user", userCommand)
}
